package be.sc.classicnetwork.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Lob;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Transient;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.Filter;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.Index;
import org.hibernate.validator.NotEmpty;
import org.hibernate.validator.NotNull;
import org.springframework.util.Assert;

import be.sc.classicnetwork.controller.validator.Expression;
import be.sc.classicnetwork.util.Utils;

/**
 * Entity for Car.
 */
@Entity
@FilterDef(name = "published", defaultCondition = "1 = published")
@Filter(name = "published")
@Expression(assertTrue = "this.priceFilled", message = "error.price")
public class Car extends AbstractEntity {

    public static final String PUBLISHED_FILTER = "published";

    private static final long serialVersionUID = -1379108734919310798L;

    @ManyToOne
    private Image thumb;

    @ManyToOne
    @NotNull
    private Make make;

    @ManyToOne
    private Type type;

    @NotEmpty
    @Index(name = "modelIndex")
    private String model;

    private Integer salesPrice;

    private String alternativeTextPrice;

    private Integer agreedPrice;

    @NotEmpty
    private String year;

    private Integer kms;

    private Integer cc;

    private String chassisNumber;

    private String engineNumber;

    private String interior;

    private String colorExterior;

    @NotEmpty
    private String drive;

    private boolean published;

    private Integer cv;

    @ManyToOne
    private Site site;

    @ManyToOne
    private Location location;

    @Lob
    private String description;

    @Lob
    private String comment;

    @ManyToOne
    private Contact contact;

    @OneToMany(cascade = { CascadeType.ALL })
    @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
    private Set<Image> images = new HashSet<Image>();

    /**
     * Temporary list to get the image uploaded (the images Set cannot use
     * index)
     */
    @Transient
    private List<Image> uploadImages = new ArrayList<Image>();

    @ManyToMany
    private Set<Option> options = new HashSet<Option>();

    /**
     * Constructor.
     */
    public Car() {
	super();
    }

    /**
     * Constructor.
     */
    public Car(String id) {
	super(id);
    }

    /**
     * addImage.
     */
    public void addImage(Image image) {
	this.images.add(image);
    }

    /**
     * Removes the specified images.
     */
    public void removeImages(Image... images) {
	Assert.notNull(images);
	for (Image image : images) {
	    // don't remove the thumb image
	    if (image.equals(this.thumb)) {
		continue;
	    }
	    this.images.remove(image);
	}
    }

    /**
     * Removes the this images that are empty.
     */
    public void removeEmptyUploadImages() {
	Assert.notNull(this.uploadImages);
	for (Iterator<Image> iterator = this.uploadImages.iterator(); iterator.hasNext();) {
	    if (iterator.next().isEmpty()) {
		iterator.remove();
	    }
	}
    }

    /**
     * Adds a default image if none image has been provided yet.
     */
    public void addDefaultImageIfNone() {
	Assert.notNull(this.images);
	if (this.images.size() == 0) {
	    addImage(new Image(Utils.getDefaultImage()));
	}
    }

    /**
     * Sets the thumb with the first image if it has not yet been set. Assert
     * that the car has at least one image.
     */
    public void setThumbIfNone() {
	Assert.notEmpty(this.images);
	if (this.thumb == null) {
	    this.thumb = this.images.iterator().next();
	}
    }

    /**
     * getImagesAsByteArray.
     */
    public Collection<byte[]> getImagesAsByteArray() {
	ArrayList<byte[]> imgs = new ArrayList<byte[]>();
	for (Image image : this.images) {
	    imgs.add(image.getFile());
	}
	return imgs;
    }

    /**
     * addOption.
     */
    public void addOption(Option option) {
	options.add(option);
    }

    /**
     * Replaces carriage return with <br>
     * (for HTML).
     */
    public String getDescriptionHtml() {
	return (description != null) ? getDescription().replaceAll("\\n", "<br>") : null;
    }

    /**
     * Checks one of sales price or alternativeTextPrice is not empty.
     */
    public boolean isPriceFilled() {
	return (salesPrice != null || !StringUtils.isEmpty(alternativeTextPrice));
    }

    public Set<Image> getImages() {
	return images;
    }

    public void setImages(Set<Image> images) {
	this.images = images;
    }

    public Integer getCc() {
	return cc;
    }

    public void setCc(Integer cc) {
	this.cc = cc;
    }

    public String getChassisNumber() {
	return chassisNumber;
    }

    public void setChassisNumber(String chassisNumber) {
	this.chassisNumber = chassisNumber;
    }

    public String getDescription() {
	return description;
    }

    public void setDescription(String description) {
	this.description = description;
    }

    public String getEngineNumber() {
	return engineNumber;
    }

    public void setEngineNumber(String engineNumber) {
	this.engineNumber = engineNumber;
    }

    public String getColorExterior() {
	return colorExterior;
    }

    public void setColorExterior(String externalColor) {
	this.colorExterior = externalColor;
    }

    public String getInterior() {
	return interior;
    }

    public void setInterior(String interiorColor) {
	this.interior = interiorColor;
    }

    public Make getMake() {
	return make;
    }

    public void setMake(Make make) {
	this.make = make;
    }

    public String getModel() {
	return model;
    }

    public void setModel(String model) {
	this.model = model;
    }

    public Integer getSalesPrice() {
	return salesPrice;
    }

    public void setSalesPrice(Integer price) {
	this.salesPrice = price;
    }

    public String getDrive() {
	return drive;
    }

    public void setDrive(String transmission) {
	this.drive = transmission;
    }

    public Integer getKms() {
	return kms;
    }

    public void setKms(Integer kms) {
	this.kms = kms;
    }

    public String getYear() {
	return year;
    }

    public void setYear(String year) {
	this.year = year;
    }

    public boolean isPublished() {
	return published;
    }

    public void setPublished(boolean published) {
	this.published = published;
    }

    public Site getSite() {
	return site;
    }

    public void setSite(Site site) {
	this.site = site;
    }

    public Image getThumb() {
	return thumb;
    }

    public void setThumb(Image thumb) {
	this.thumb = thumb;
    }

    public Set<Option> getOptions() {
	return options;
    }

    public void setOptions(Set<Option> options) {
	this.options = options;
    }

    public Integer getCv() {
	return cv;
    }

    public void setCv(Integer cv) {
	this.cv = cv;
    }

    public Type getType() {
	return type;
    }

    public void setType(Type type) {
	this.type = type;
    }

    public Contact getContact() {
	return contact;
    }

    public void setContact(Contact contact) {
	this.contact = contact;
    }

    public Location getLocation() {
	return location;
    }

    public void setLocation(Location location) {
	this.location = location;
    }

    public Integer getAgreedPrice() {
	return agreedPrice;
    }

    public void setAgreedPrice(Integer agreedPrice) {
	this.agreedPrice = agreedPrice;
    }

    public String getComment() {
	return comment;
    }

    public void setComment(String myDescription) {
	this.comment = myDescription;
    }

    public String getAlternativeTextPrice() {
	return alternativeTextPrice;
    }

    public void setAlternativeTextPrice(String alternativeTextPrice) {
	// set the price as number or alternative text
	if (NumberUtils.isNumber(alternativeTextPrice)) {
	    this.alternativeTextPrice = null;
	    this.salesPrice = NumberUtils.createInteger(alternativeTextPrice);
	} else {
	    this.salesPrice = null;
	    this.alternativeTextPrice = alternativeTextPrice;
	}
    }

    public List<Image> getUploadImages() {
	return uploadImages;
    }

    public void setUploadImages(List<Image> uploadImages) {
	this.uploadImages = uploadImages;
    }

}
